// Functions associated with the Audio class
//-----------------

//-----------------
// Includes libraries and other files
#include "Audio.h"
//-----------------

//-----------------
//// CONSTRUCTOR - Audio
Audio::Audio()
	: g_system_( nullptr), sound_( nullptr), channel_( nullptr), pos_( 0, 0, 0), filename_( ""), mute_( false), loop_count_( 0)
{
	FMOD::System_Create( &g_system_);
	g_system_->init( 100, FMOD_INIT_NORMAL, 0);
}

//// CONSTRUCTOR - Audio
Audio::Audio( const std::string& filename, const int loop_count)
	: g_system_( nullptr), sound_( nullptr), channel_( nullptr), pos_( 0, 0, 0), filename_( filename), mute_( false), loop_count_( loop_count)
{
	FMOD::System_Create( &g_system_);
	g_system_->init( 100, FMOD_INIT_NORMAL, 0);
}

//// CONSTRUCTOR - Audio
Audio::Audio( const Audio& a)
	: g_system_( nullptr), sound_( nullptr), channel_( nullptr), pos_( a.pos_), filename_( a.filename_), mute_( a.mute_), loop_count_( a.loop_count_)
{
	FMOD::System_Create( &g_system_);
	g_system_->init( 100, FMOD_INIT_NORMAL, 0);
}

//// DESTRUCTOR - ~Audio
Audio::~Audio() {
	if( sound_ != nullptr)
		sound_->release();

	if( g_system_ != nullptr)
		g_system_->release();
}
//-----------------

//-----------------
//// FUNCTION - is_playing
// return whether or not the sound is playing
bool Audio::is_playing() const {
	bool playing;
	channel_->isPlaying( &playing);
	return playing;
}

//// FUNCTION - is_mute
// return whether or not the sound is muted
bool Audio::is_mute() const {
	return mute_;
}

//// FUNCTION - create_3d_sound
// create a 3D sound at a specified location
void Audio::create_3d_sound( const float& x, const float& y, const float& z) {
	if( g_system_ == nullptr)
		throw fmod_exception();

	// create the sound
	fmod_assert( g_system_->createSound( filename_.c_str(), FMOD_3D, 0, &sound_));

	// set the sound's minimum and maximum distance attenuation
	fmod_assert( sound_->set3DMinMaxDistance( 10.0f, 10000.0f));

	// allocate a channel
	fmod_assert( g_system_->playSound( FMOD_CHANNEL_FREE, sound_, true, &channel_));

	// switch the channel to looping
	fmod_assert( channel_->setMode( FMOD_LOOP_NORMAL));
	fmod_assert( channel_->setLoopCount( loop_count_));

	// call the update function to set the channel
	set_pos( x, y, z);

	//g_system_->set3DListenerAttributes
}

//// FUNCTION - create_3d_sound
// create a 3D sound at a specified location
void Audio::create_3d_sound( const float& x, const float& y, const float& z, const std::string& filename, int loop_count) {
	if( g_system_ == nullptr)
		throw fmod_exception();

	// create the sound
	fmod_assert( g_system_->createSound( filename.c_str(), FMOD_3D, 0, &sound_));

	// set the sound's minimum and maximum distance attenuation
	fmod_assert( sound_->set3DMinMaxDistance( 10.0f, 10000.0f));

	// allocate a channel
	fmod_assert( g_system_->playSound( FMOD_CHANNEL_FREE, sound_, true, &channel_));

	// switch the channel to looping
	fmod_assert( channel_->setMode( FMOD_LOOP_NORMAL));
	fmod_assert( channel_->setLoopCount( loop_count));

	// call the update function to set the channel
	set_pos( x, y, z);

	//g_system_->set3DListenerAttributes
}

//// FUNCTION - create_sound
// create a sound based on the instance's filename and loop counter
void Audio::create_sound() {
	fmod_assert( g_system_->createSound( filename_.c_str(), FMOD_DEFAULT, 0, &sound_));

	// allocate a channel
	fmod_assert( g_system_->playSound( FMOD_CHANNEL_FREE, sound_, true, &channel_));

	// switch the channel to looping
	fmod_assert( channel_->setMode( FMOD_LOOP_NORMAL));
	fmod_assert( channel_->setLoopCount( loop_count_));
}

//// FUNCTION - create_sound
// create a sound based on parameters
void Audio::create_sound( const std::string& filename, int loop_count) {
	fmod_assert( g_system_->createSound( filename.c_str(), FMOD_DEFAULT, 0, &sound_));

	// allocate a channel
	fmod_assert( g_system_->playSound( FMOD_CHANNEL_FREE, sound_, true, &channel_));

	// switch the channel to looping
	fmod_assert( channel_->setMode( FMOD_LOOP_NORMAL));
	fmod_assert( channel_->setLoopCount( loop_count));
}

//// FUNCTION - set_pos
// set the position of the 3D sound
void Audio::set_pos( const float& x, const float& y, const float& z) {
	pos_.x = x;
	pos_.y = y;
	pos_.z = z;

	// update the 3D attribute of the sound slip
	FMOD_VECTOR p, v;
	p.x = pos_.x;
	p.y = pos_.y;
	p.z = pos_.z;
	v.x = 0;
	v.y = 0;
	v.z = 0;

	// set 3D attributes
	fmod_assert( channel_->set3DAttributes( &p, &v));
}

//// FUNCTION - set_filename
// set the name of the audio file
void Audio::set_filename( const std::string& fn) {
	filename_ = fn;
}

//// FUNCTION - update_channel
// update the audio channels
void Audio::update_channels() {
	g_system_->update();
}

//// FUNCTION - play
// play the sound
void Audio::play() {
	bool p;
	channel_->getPaused( &p);
	// start sound playing if it's not already playing
	if( p)
		fmod_assert( channel_->setPaused( false));
}

//// FUNCTION - pause
// pause the sound
void Audio::pause() {
	bool p;
	channel_->getPaused( &p);
	// pause the sound if it's not already paused
	if( p)
		fmod_assert( channel_->setPaused( true));
}

//// FUNCTION - stop
// stop playing the sound and set its playback position to 0
void Audio::stop() {
	if( is_playing()) {
		fmod_assert( channel_->setPaused( true));
		fmod_assert( channel_->setPosition( 0, FMOD_TIMEUNIT_MS));
	}
}

//// FUNCTION - set_play_pos
// set the playback position of the sound
void Audio::set_play_pos( const int s) {
	// If there is a large sound file playing then this means you can 'skip' back and forth along it
	fmod_assert( channel_->setPosition( s, FMOD_TIMEUNIT_MS));
}

//// FUNCTION - mute
// mute the sound
void Audio::mute() {
	// This mutes the channel, however if the sound is being repeatedly created then it's useless.
	// Such examples of when this is useless is gunfire and footsteps.
	mute_ = true;
	channel_->setMute( true);
}

//// FUNCTION - unmute
// unmute the sound
void Audio::unmute() {
	mute_ = false;
	channel_->setMute( false);
}

//// FUNCTION - fmod_assert
// used to catch errors from FMOD
void Audio::fmod_assert( FMOD_RESULT result) {
	if( result != FMOD_OK) {
		std::stringstream ss;
		ss << "FMOD returned error " << result << " : " << FMOD_ErrorString( result) << std::endl << "Error Code: 003";
		MessageBox( NULL, ss.str().c_str(), "FMOD Error", MB_OK | MB_ICONWARNING);
		throw fmod_exception();
	}
}